/**
 * \file Thread.cc
 * \brief Thread facilities
 * \author Corey Tabaka
 */

/*
   Copyright 2006 Corey Tabaka

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include <ktypes.h>
#include <platform/Descriptor.h>
#include <platform/Interrupt.h>
#include <platform/Memory.h>
#include <platform/Timer.h>
#include <platform/RegUtil.h>
#include <platform/processor.h>
#include <Thread.h>
#include <Process.h>
#include <Namespace.h>
#include <Log.h>
#include <HandleMap.h>
#include <assert.h>
#include <malloc.h>
#include <syscall.h>

#include <i386/hw-instr.h>
#include <i386/mem.h>
#include <i386/console.h>
#include <string.h>
#include <stdio.h>

#define USE_KSTACK_GUARD_PAGE 1

DEF_NULL_REF(Thread);

/**
 * Next thread id
 */
static tid_t nextTid;

/**
 * Thread id to thread reference hashtable
 */
static HandleMap<RefThread> threadList;

/**
 * Returns the next thread id in sequence that isn't already in the thread list
 */
static tid_t getNextTid(void);

/**
 * The currently executing thread. It is a bug for this to ever be null once
 * thread scheduling begins. Thread operations should always assign a valid
 * pointer to another thread when changing the current thread.
 */
static Thread *currentThread;

/**
 * System idle thread
 */
static RefThread idleThread;

/**
 * Queue type enumeration
 */
enum {
	QUEUE_NONE				=-1,
	QUEUE_IDLE				= 0,
	QUEUE_MIN_PRIORITY		= 1,
	QUEUE_NORMAL_PRIORITY	= 32,
	QUEUE_MAX_PRIORITY		= 63,
	QUEUE_SLEEP,
	QUEUE_WAIT,
	
	NUM_QUEUES
};

/**
 * Thread ready queues for each priority
 */
static Thread *head[NUM_QUEUES], *tail[NUM_QUEUES];
static InterruptLock queueLocks[NUM_QUEUES];
static InterruptLock rescheduleLock;

/*
 * Queue ops with locks
 */
#define QUEUE_LOCK_REMOVE(n, q)		\
	do {							\
		queueLocks[q].lock();		\
		Thread::queueRemove(n, q);	\
		queueLocks[q].unlock();		\
	} while (false)

#define QUEUE_LOCK_APPEND(n, q)		\
	do {							\
		queueLocks[q].lock();		\
		Thread::queueAppend(n, q);	\
		queueLocks[q].unlock();		\
	} while (false)

#define QUEUE_LOCK_PREPEND(n, q)	\
	do {							\
		queueLocks[q].lock();		\
		Thread::queuePrepend(n, q);	\
		queueLocks[q].unlock();		\
	} while (false)
	
// defined in kernel.ld
extern byte kstack[];

/**
 * System TSS and TSS selector
 */
static tss_t systemTss;
static seg_sel_t systemTssSelector;

/** System stack selector
 * Used when an exception occurs in an exception handler
 */
static seg_sel_t systemStackSelector;

/**
 * Pointer to the last thread that used the fpu. Used to lazily save fpu/mmx/sse
 * context only when another thread is about to clobber the fpu context.
 */
static Thread *lastFpuThread;

/**
 * Pointer to FPU context save function, depending on CPU feature support.
 */
static void (*fpuContextSave)(fpu_context_t *);

/**
 * Pointer to FPU context restore function, depending on CPU feature support.
 */
static void (*fpuContextRestore)(fpu_context_t *);

void Thread::idle(void *) {
	// this is the first opportunity to calibrate the timers, since this thread
	// executes immediately after interrupts are first enabled at the end of
	// initialization
	Timer::calibrateTimers();
		
	Interrupt::disableLocalInterrupts();
	QUEUE_LOCK_REMOVE(idleThread, idleThread->queue);
	
	idleThread->queue = QUEUE_IDLE;
	idleThread->priority = THREAD_PRIORITY_IDLE;
	
	QUEUE_LOCK_APPEND(idleThread, idleThread->queue);
	Interrupt::enableLocalInterrupts();
	
	uint32 count = 0;
	while (true) {
		printf_xy(71, 1, WHITE, "%08x", count++);
		
		hlt();
	}
}

void Thread::initialize(void) {
	Interrupt::registerHandler(INT_PIT, tickHandler);
	Interrupt::registerHandler(INT_SYSCALL, syscallHandler);
	
	Interrupt::registerHandler(INT_DEV_NA_EX, faultHandler);
	Interrupt::registerHandler(INT_DIVIDE_0, faultHandler);
	Interrupt::registerHandler(INT_PAGE_FAULT, faultHandler);
	Interrupt::registerHandler(INT_GP_FAULT, faultHandler);
	Interrupt::registerHandler(INT_STACK_FAULT, faultHandler);
	Interrupt::registerHandler(INT_INVALID_OP, faultHandler);
	Interrupt::registerHandler(INT_DEBUG_EX, faultHandler);
		
	// TSS descriptor setup
	memset(&systemTss, 0, sizeof(tss_t));
	
	// allocate a descriptor for the system's TSS
	systemTssSelector = Descriptor::allocGlobalDescriptor();
	assert(systemTssSelector != 0);
	
	// setup up the system TSS descriptor
	Descriptor::setGlobalDescriptor(systemTssSelector, &systemTss,
		sizeof(tss_t), 1, 0, 0, SEG_TYPE_TSS, 0, 0);
	
	// allocate a descriptor for the kernel stacks
	systemStackSelector = Descriptor::allocGlobalDescriptor();
	
	// setup the kernel stack descriptor, used by all kernel stacks
	Descriptor::setGlobalDescriptor(systemStackSelector, 0x00000000, 0xfffff, 1,
		0, 1, SEG_TYPE_DATA_RW, 1, 1);
	
	systemTss.esp0 = (uint32) ::kstack;
	systemTss.ss0 = DATA_SELECTOR;//systemStackSelector;
	systemTss.ss1 = 0;
	systemTss.ss2 = 0;
	systemTss.eflags = 0x00003002;
	systemTss.bitmap = offsetof(tss_t,tss_bitmap);
	
	// load task register with current system tss
	loadTR(systemTssSelector);
	
	// allow for SSE/SSE2/SSE3
	if (Namespace::isKey("hw.cpu.fxsr") && Namespace::getInt("hw.cpu.fxsr")) {
		fpuContextSave = fpu_save_sse;
		fpuContextRestore = fpu_restore_sse;
		setInCR4(X86_CR4_OSFXSR);
	} else {
		fpuContextSave = fpu_save;
		fpuContextRestore = fpu_restore;
	}
	
	// fault on fpu/mmx/sse
	clearInCR0(X86_CR0_MP | X86_CR0_EM);
	
	Thread *t = new Thread(idle, 0, "System Idle", Process::getSystemProcess());
	t->priority = t->floatingPriority = THREAD_PRIORITY_MAX;
	t->queue = QUEUE_MAX_PRIORITY;
	Thread::queueAppend(t, QUEUE_MAX_PRIORITY);
		
	idleThread = t->self;
	
	systemTss.esp0 = (uint32) t->kstackTop;
	setInCR0(X86_CR0_TS); // fault on fpu/mms/sse
}

/**
 * Gets things going by bootstrapping the idle thread
 */
void Thread::begin(void) {
	static bool once = false;
	
	if (!once) {
		once = true;
		
		currentThread = idleThread;
		
		asm volatile (
			"mov %0,%%esp\n\t"
			"popa\n\t"
			"pop %%ds\n\t"
			"pop %%es\n\t"
			"pop %%fs\n\t"
			"pop %%gs\n\t"
			"add $0x8,%%esp\n\t"
			"iret"
			:
			: "a" (idleThread->kstackTop)
			:
		);
	}
}

RefThread Thread::createThread(ThreadEntryPoint entry, void *arg,
	const char *name, RefProcess process) {
	
	Thread * t = new Thread(entry, arg, name, process);
	return t->self;
}

RefThread UserThread::createThread(ThreadEntryPoint entry, void *arg,
	const char *name, RefProcess process) {
	
	UserThread * t = new UserThread(entry, arg, name, process);
	return t->self;
}

RefThread VM86Thread::createThread(ThreadEntryPoint entry, void *arg,
	const char *name, RefProcess process) {
	
	VM86Thread *t = new VM86Thread(entry, arg, name, process);
	return t->self;
}

RefThread Thread::getCurrentThread(void) {
	return currentThread->self;
}

void Thread::queueRemove(Thread *node, int queue) {
	bool isHead = node == head[queue];
	bool isTail = node == tail[queue];
	
	if (isTail) {
		tail[queue] = node->prev;
		
		if (!isHead) {
			node->prev->next = 0;
		}
	}
	
	if (isHead) {
		head[queue] = node->next;
		
		if (!isTail) {
			node->next->prev = 0;
		}
	}
	
	if (!isHead && !isTail) {
		node->prev->next = node->next;
		node->next->prev = node->prev;
	}
	
	// paranoid
	node->next = node->prev = 0;
}

void Thread::queueAppend(Thread *node, int queue) {
	node->next = 0;
	
	if (head[queue]) {
		node->prev = tail[queue];
		tail[queue]->next = node;
	} else {
		node->prev = 0;
		head[queue] = node;
	}
	
	tail[queue] = node;
}

void Thread::queuePrepend(Thread *node, int queue) {
	node->next = head[queue];
	node->prev = 0;
	
	if (head[queue]) {
		head[queue]->prev = node;
	} else {
		tail[queue] = node;
	}
	
	head[queue] = node;
}

Thread::Thread(thread_type_t _type) : type(_type), self(this, true), lock() {
}

Thread::Thread(ThreadEntryPoint entry, void *arg, const char *name,
	RefProcess process) : type(THREAD_TYPE_KERNEL), self(this, true), lock() {
	this->process = process;
	strcpy(this->name, name ? name : "Unnamed");
		
	//Log::printf(LOG_LOW, "Thread '%s' in process '%s' entry at %08x\n", name,
	//	process->getName(), (uint) entry);
	
	priority = floatingPriority = THREAD_PRIORITY_NORMAL;
	
	queue = QUEUE_NONE;
	next = prev = 0;
	
	tid = getNextTid();
	threadList.put(tid, self);
	process->threads.put(tid, self);	
	
	// alloc the fpu save region
	fpuContext = (fpu_context_t *) memalign(16, sizeof(fpu_context_t));
	fpuState = FPU_STATE_UNUSED;
	
	// allocate kernel mode stack with page alignment
#if USE_KSTACK_GUARD_PAGE
	uint32 *guard = (uint32 *) Memory::alloc(THREAD_KSTACK_SIZE + PAGE_SIZE*2);
	kstack = guard + PAGE_SIZE/sizeof(uint32);
	process->getPageTable().acquire(guard, guard, PAGE_SIZE, false,
		ACQUIRE_MODE_KEEP_PDE
		| ACQUIRE_MODE_REPLACE_PTE
		| ACQUIRE_MODE_READ_ONLY);
	guard = kstack + THREAD_KSTACK_SIZE/sizeof(uint32);
	process->getPageTable().acquire(guard, guard, PAGE_SIZE, false,
		ACQUIRE_MODE_KEEP_PDE
		| ACQUIRE_MODE_REPLACE_PTE
		| ACQUIRE_MODE_READ_ONLY);
#else
	kstack = (uint32 *) Memory::alloc(THREAD_KSTACK_SIZE);
#endif

	// fill in the entry record in the new thread's kernel stack frame so the
	// scheduler can correctly switch to this stack
	kstackTop = kstack + THREAD_KSTACK_SIZE/sizeof(uint32) - 20;
	uint32 *stack = kstackTop;
	savedStackPointer = (uint32) kstackTop;
	
	stack[18] = (uint32) arg; // task argument
	stack[17] = 0xffffffff;//(uint32) task_exit; // supervisor mode cleanup
	
	stack[16] = 0x3202;					// eflags (IF = 1, NT = 1, IOPL = 3)
	stack[15] = CODE_SELECTOR;			// cs for kernel priv
	stack[14] = (uint32) entry;			// eip = disbatch function
	stack[13] = 0;						// err_code
	stack[12] = 0;						// which_int
	stack[11] = DATA_SELECTOR;			// gs
	stack[10] = DATA_SELECTOR;			// fs
	stack[ 9] = DATA_SELECTOR;			// es
	stack[ 8] = DATA_SELECTOR;			// ds
	stack[ 7] = 0;						// eax
	stack[ 6] = 0;						// ecx
	stack[ 5] = 0;						// edx
	stack[ 4] = 0;						// ebx
	stack[ 3] = 0;						// esp
	stack[ 2] = 0;						// ebp
	stack[ 1] = 0;						// esi
	stack[ 0] = 0;						// edi
	
	/*printf("Thread '%s' in process '%s' entry=%08x, kstack=%08x, ssp=%08x\n",
		name, process->getName(), (uint) entry, (uint) kstack,
		(uint32) &savedStackPointer);*/
}

Thread::~Thread(void) {
	Log::printf(LOG_LOW, "%s:%d %s\n", __FUNCTION__, __LINE__, name);
	//printf("%s:%d %s\n", __FUNCTION__, __LINE__, name);
			
	// see if lastFpuThread needs to be cleaned up to avoid dangling pointers
	if (lastFpuThread == this) {
		lastFpuThread = 0;
	}
	
	free(fpuContext);
	
#if USE_KSTACK_GUARD_PAGE
	uint32 *guard = kstack + THREAD_KSTACK_SIZE/sizeof(uint32);
	process->getPageTable().acquire(guard, guard, PAGE_SIZE, false,
		ACQUIRE_MODE_KEEP_PDE
		| ACQUIRE_MODE_REPLACE_PTE);
	guard = kstack - PAGE_SIZE/sizeof(uint32);
	process->getPageTable().acquire(guard, guard, PAGE_SIZE, false,
		ACQUIRE_MODE_KEEP_PDE
		| ACQUIRE_MODE_REPLACE_PTE);
	Memory::free(guard, THREAD_KSTACK_SIZE + PAGE_SIZE*2);
#else
	Memory::free(kstack, THREAD_KSTACK_SIZE);
#endif
}

UserThread::UserThread(ThreadEntryPoint entry, void *arg, const char *name,
	RefProcess process) : Thread(THREAD_TYPE_USER), local() {
	this->process = process;
	strcpy(this->name, name ? name : "Unnamed");
		
	//Log::printf(LOG_LOW, "Thread '%s' in process '%s' entry at %08x\n", name,
	//	process->getName(), (uint) entry);
	
	priority = floatingPriority = THREAD_PRIORITY_NORMAL;
	
	queue = QUEUE_NONE;
	next = prev = 0;
	
	tid = getNextTid();
	threadList.put(tid, self);
	process->threads.put(tid, self);
	
	
	// init TLS ordinal counter
	ordinal = 0;
	
	// alloc the fpu save region
	fpuContext = (fpu_context_t *) memalign(16, sizeof(fpu_context_t));
	fpuState = FPU_STATE_UNUSED;
	
	// allocate kernel mode stack with page alignment
#if USE_KSTACK_GUARD_PAGE
	uint32 *guard = (uint32 *) Memory::alloc(THREAD_KSTACK_SIZE + PAGE_SIZE*2);
	kstack = guard + PAGE_SIZE/sizeof(uint32);
	process->getPageTable().acquire(guard, guard, PAGE_SIZE, false,
		ACQUIRE_MODE_KEEP_PDE
		| ACQUIRE_MODE_REPLACE_PTE
		| ACQUIRE_MODE_READ_ONLY);
	guard = kstack + THREAD_KSTACK_SIZE/sizeof(uint32);
	process->getPageTable().acquire(guard, guard, PAGE_SIZE, false,
		ACQUIRE_MODE_KEEP_PDE
		| ACQUIRE_MODE_REPLACE_PTE
		| ACQUIRE_MODE_READ_ONLY);
#else
	kstack = (uint32 *) Memory::alloc(THREAD_KSTACK_SIZE);
#endif
	
	// allocate virtual address space for the full size stack
	if (!process->getPageMap().alloc(&ustackEa, THREAD_USTACK_SIZE)) {
		panic("Failed to allocate user stack virtual address: %s\n", name);
	}
	uint32 ustack_top = ustackEa + THREAD_USTACK_SIZE;
	
	// init the segment mapping
	memset(segmentMapping, 0, sizeof(segmentMapping));
	nextSegment = 0;
	
	// allocate a segment for the user mode stack
	growStack();
	ustack = (uint32 *) segmentMapping[0]; // use the first segment
	
	// fill in the basic user stack values
	uint32 *params = ustack + THREAD_USTACK_INCR/sizeof(uint32) - 2;
	
	params[1] = (uint32) arg; // thread argument
	params[0] = PROCESS_USER_SPACE_EXIT; // non-supervisor thread return address
	
	// fill in the entry record in the new thread's kernel stack frame so the
	// scheduler can correctly switch to this stack
	kstackTop = kstack + THREAD_KSTACK_SIZE/sizeof(uint32) - 20;
	savedStackPointer = (uint32) kstackTop;
	uint32 *stack = kstackTop;
	
	stack[18] = USER_DATA_SELECTOR;				// user_ss
	stack[17] = ustack_top - 2*sizeof(uint32);	// user_esp
	
	stack[16] = 0x3202;					// eflags (IF = 1, NT = 1, IOPL = 3)
	stack[15] = USER_CODE_SELECTOR;		// cs, this will change for multiple priv levels
	stack[14] = (uint32) entry;			// eip = disbatch function
	stack[13] = 0;						// err_code
	stack[12] = 0;						// which_int
	stack[11] = USER_DATA_SELECTOR;		// gs
	stack[10] = USER_DATA_SELECTOR;		// fs
	stack[ 9] = USER_DATA_SELECTOR;		// es
	stack[ 8] = USER_DATA_SELECTOR;		// ds
	stack[ 7] = 0;						// eax
	stack[ 6] = 0;						// ecx
	stack[ 5] = 0;						// edx
	stack[ 4] = 0;						// ebx
	stack[ 3] = 0;						// esp
	stack[ 2] = 0;						// ebp
	stack[ 1] = 0;						// esi
	stack[ 0] = 0;						// edi
	
	/*printf("UserThread '%s' in process '%s' entry=%08x, kstack=%08x, "
		"ssp=%08x\n", name, process->getName(), (uint) entry, (uint) kstack,
		(uint32) &savedStackPointer);*/
}

UserThread::~UserThread(void) {
	Log::printf(LOG_LOW, "%s:%d\n", __FUNCTION__, __LINE__);
	//printf("%s:%d\n", __FUNCTION__, __LINE__);
	
	ustack = 0;
	while (nextSegment > 0) {
		shrinkStack();
	}
	
	process->getPageMap().free(ustackEa, THREAD_USTACK_SIZE);
}

extern uint8 *lowMemImage;

VM86Thread::VM86Thread(ThreadEntryPoint entry, void *arg, const char *name,
	RefProcess process) : Thread(THREAD_TYPE_VM86) {
	this->process = process;
	strcpy(this->name, name ? name : "Unnamed");
		
	//Log::printf(LOG_LOW, "Thread '%s' in process '%s' entry at %08x\n", name,
	//	process->getName(), (uint) entry);
	
	priority = floatingPriority = THREAD_PRIORITY_NORMAL;
	
	queue = QUEUE_NONE;
	next = prev = 0;
	
	tid = getNextTid();
	threadList.put(tid, self);
	process->threads.put(tid, self);	
	
	// alloc the fpu save region
	fpuContext = (fpu_context_t *) memalign(16, sizeof(fpu_context_t));
	fpuState = FPU_STATE_UNUSED;
	
	// look for an area covering 0M through 1M+64k or create one
	Region *region = process->getAddressSpace().findType(REGION_TYPE_VM86);
	
	if (!region) {
		//printf("%s: creating new area\n", __FUNCTION__);
		region = process->getAddressSpace().create(0, 0, (1024 + 64) * 1024,
			REGION_FLAGS_CONTIG | REGION_FLAGS_USER | REGION_FLAGS_WIRED,
			REGION_TYPE_VM86);
	}
	
	#if 1
	// remap the BIOS area to the real address (identity map)
	process->getPageTable().acquire((void *) 0xa0000, (void *) 0xa0000,
		0x60000, true, ACQUIRE_MODE_REPLACE_PTE | ACQUIRE_MODE_KEEP_PDE);
	
	// copy the real IVT and BDA (0-0x500) into the VM
	memcpy((void *) region->getPhysicalBase(), (void *) lowMemImage, 0x500);
	/*printf("[0x10] %04x at %08x\n",
		((uint16 *) process->getPhysicalBase())[0x10*2],
		(uint32) process->getPhysicalBase());*/
	
	// copy the extended BDA (0x9fc00-0x9ffff) into the VM
	memcpy((uint8 *) region->getPhysicalBase() + 0x9fc00,
		(void *) (lowMemImage + 0x9fc00), 0x500);
	#endif
	
	// allocate kernel mode stack with page alignment
#if USE_KSTACK_GUARD_PAGE
	uint32 *guard = (uint32 *) Memory::alloc(THREAD_KSTACK_SIZE + PAGE_SIZE*2);
	kstack = guard + PAGE_SIZE/sizeof(uint32);
	process->getPageTable().acquire(guard, guard, PAGE_SIZE, false,
		ACQUIRE_MODE_KEEP_PDE
		| ACQUIRE_MODE_REPLACE_PTE
		| ACQUIRE_MODE_READ_ONLY);
	guard = kstack + THREAD_KSTACK_SIZE/sizeof(uint32);
	process->getPageTable().acquire(guard, guard, PAGE_SIZE, false,
		ACQUIRE_MODE_KEEP_PDE
		| ACQUIRE_MODE_REPLACE_PTE
		| ACQUIRE_MODE_READ_ONLY);
#else
	kstack = (uint32 *) Memory::alloc(THREAD_KSTACK_SIZE);
#endif
	
	// fill in the entry record in the new task's kernel stack frame
	// so the scheduler can correctly switch to this stack
	kstackTop = kstack + THREAD_KSTACK_SIZE/sizeof(uint32) - 24;
	savedStackPointer = (uint32) kstackTop;
	uint32 *stack = kstackTop;
	
	stack[22] = 0x0050;	// vm_gs
	stack[21] = 0x0050;	// vm_fs
	stack[20] = 0x0050;	// vm_ds
	stack[19] = 0x0050;	// vm_es
	stack[18] = 0x0050;	// user_ss/vm_ss
	stack[17] = 0xfffe;	// user_esp/vm_esp
	
	this->vif = true; // virtual IF flag = 1
	
	stack[16] = 0x20202;				// eflags (VM = 1, IF = 1, NT = 1, IOPL = 3)
	stack[15] = 0x0050;					// cs,
	stack[14] = (uint32) entry;			// eip = disbatch function
	stack[13] = 0;						// err_code
	stack[12] = 0;						// which_int
	stack[11] = 0;						// gs
	stack[10] = 0;						// fs
	stack[ 9] = 0;						// es
	stack[ 8] = 0;						// ds
	stack[ 7] = 0;						// eax
	stack[ 6] = 0;						// ecx
	stack[ 5] = 0;						// edx
	stack[ 4] = 0;						// ebx
	stack[ 3] = 0;						// esp
	stack[ 2] = 0;						// ebp
	stack[ 1] = 0;						// esi
	stack[ 0] = 0;						// edi
	
	/*printf("VM86Thread '%s' in process '%s' entry=%08x, kstack=%08x, "
		"ssp=%08x\n", name, process->getName(), (uint) entry, (uint) kstack,
		(uint32) &savedStackPointer);*/
}

VM86Thread::~VM86Thread(void) {
	Log::printf(LOG_LOW, "%s:%d\n", __FUNCTION__, __LINE__);
	//printf("%s:%d\n", __FUNCTION__, __LINE__);
}

void UserThread::growStack(void) {
	if (nextSegment < THREAD_USTACK_SIZE/THREAD_USTACK_INCR) {
		uint32 seg = (uint32) Memory::alloc(THREAD_USTACK_INCR);
		
		if (seg) {
			segmentMapping[nextSegment] = seg;
			uint32 ea = ustackEa + THREAD_USTACK_SIZE - THREAD_USTACK_INCR
				* (nextSegment + 1);
			
			process->getPageTable().acquire((void *) seg, (void *) ea,
				THREAD_USTACK_INCR, true, ACQUIRE_MODE_REPLACE_PTE);
			
			//printf("Growing stack (%s) pa=%08x, ea=%08x, seg=%u\n", name, seg,
			//	ea, nextSegment);
			/*Log::printf(LOG_HIGH, "Growing stack (%s) pa=%08x, ea=%08x, seg=%u"
				"\n", name, seg, ea, nextSegment);*/
			
			nextSegment++;
		} else {
			panic("Failed to allocate user stack segment %d", nextSegment);
			/*Log::printf(LOG_HIGH, "Failed to allocate user stack segment %d",
				nextSegment);
			kill();*/
		}
	} else {
		bug("Attempt to grow stack (%s) beyond max user stack size", name);
	}
}

void UserThread::shrinkStack(void) {
	if (nextSegment > 0) {
		nextSegment--;
		uint32 seg = segmentMapping[nextSegment];
		segmentMapping[nextSegment] = 0;
		
		process->getPageTable().release((void *) seg, THREAD_USTACK_INCR);
		Memory::free((void *) seg, THREAD_USTACK_INCR);
	} else {
		bug("Attempt to shrink user stack (%s) below 0 segments", name);
	}
}

bool UserThread::adjustStack(regs_t *regs) {
	uint32 to = getCR2();
	page_entry_t code;
	code.page = (void *) regs->err_code;
	
	/*Log::printf(LOG_LOW, "Page fault at %08x to %08x %s! [%c %c %c]\n",
		regs->eip, to, this->name,
		code.present ? 'p' : ' ',
		code.rw ? 'w' : 'r',
		code.user_priv ? 'u' : 's');*/
	
	//return false;
	
	if (to >= ustackEa && to < ustackEa + THREAD_USTACK_SIZE) {
		// TODO: more validation of the growth event such as making sure the
		// access was from a page not present and that the referenced stack
		// segment has not actually been allocated yet
		
		growStack();
		
		return true;
	} else {
		return false;
	}
}

void *UserThread::getLocal(uint32 key) {
	RefUserTls tls(0);
	if (local.get(key, tls)) {
		return tls->data;
	} else {
		return 0;
	}
}

void UserThread::setLocal(uint32 key, void *data) {
	RefUserTls tls(0);
	if (local.get(key, tls)) {
		tls->data = data;
	}
}

uint32 UserThread::createKey(void *data, void (*destructor)(void *)) {
	RefUserTls tls = RefUserTls(new UserTls);
	tls->data = data;
	tls->destructor = destructor;
	
	lock.lock();
	while (local.containsHandle(ordinal)) {
		ordinal++;
	}
	uint32 key = ordinal;
	lock.unlock();
	
	local.put(key, tls);
	return key;
}

void UserThread::destroyKey(uint32 key) {
	local.remove(key);
}

tid_t Thread::getTid(void) {
	return tid;
}

const char *Thread::getName(void) {
	return name;
}

RefProcess Thread::getProcess(void) {
	return process;
}

int *Thread::getErrno(void) {
	return &errno;
}

void Thread::save(regs_t *regs) {
	savedStackPointer = regs->task_esp;
}

void Thread::restore(regs_t *regs) {
	regs->task_esp = savedStackPointer;
	savedStackPointer = 0;
	systemTss.esp0 = (uint32) kstackTop;
	
	if (fpuState & FPU_STATE_USED) {
		clearInCR0(X86_CR0_TS); // don't fault on fpu/mmx/sse
		
		// save the context for the previous task to use the fpu if not this one
		if (lastFpuThread && lastFpuThread != this) {
			fpuContextSave(lastFpuThread->fpuContext);
			lastFpuThread->fpuState |= FPU_STATE_SAVED;
		}
		
		lastFpuThread = this;
		
		// if another task caused this context to be saved, restore it
		if (fpuState & FPU_STATE_SAVED) {
			fpuContextRestore(lastFpuThread->fpuContext);
			fpuState &= ~FPU_STATE_SAVED;
		}
	} else {
		setInCR0(X86_CR0_TS); // fault on fpu/mms/sse
	}
	
	if (process) {
		process->getPageTable().makeCurrent();
	} else {
		bug("%s:%d %s: No parent process!", __FILE__, __LINE__, __FUNCTION__);
	}
}

int Thread::getPriority(void) {
	return priority;
}

void Thread::setPriority(int p) {
	if (p >= THREAD_PRIORITY_MIN && p <= THREAD_PRIORITY_MAX) {
		priority = p;
	}
}

int Thread::getFloatingPriority(void) {
	return floatingPriority;
}

void Thread::setFloatingPriority(int fp) {
	if (fp >= THREAD_PRIORITY_MIN && fp <= THREAD_PRIORITY_MAX) {
		floatingPriority = fp;
	}
}

void Thread::run(void) {
	lock.lock();
	
	if (queue == QUEUE_NONE) {
		queue = getFloatingPriority();
		
		QUEUE_LOCK_APPEND(this, queue);
	}
	
	lock.unlock();
}

void Thread::kill(void) {
	lock.lock();
	
	if (queue != QUEUE_NONE) {
		QUEUE_LOCK_REMOVE(this, queue);
		
		queue = QUEUE_NONE;
		
		//RefThread thread = self;
		
		// this will cause the thread to be deallocated if there are no other
		// references to it
		threadList.remove(tid);
		process->threads.remove(tid);
		
		lock.unlock();
		switchThreads();
		
		return;
	}
	
	lock.unlock();
}

void Thread::wait(void) {
	lock.lock();
	
	if (queue <= QUEUE_MAX_PRIORITY) {
		QUEUE_LOCK_REMOVE(this, queue);
		
		queue = QUEUE_WAIT;
		
		QUEUE_LOCK_APPEND(this, queue);
		
		lock.unlock();
		switchThreads();
		
		return;
	}
	
	lock.unlock();
}

void Thread::notify(void) {
	lock.lock();
	
	if (queue == QUEUE_WAIT) {
		QUEUE_LOCK_REMOVE(this, queue);
		
		queue = getFloatingPriority();
		
		QUEUE_LOCK_APPEND(this, queue);
		
		lock.unlock();
		switchThreads();
		
		return;
	}
	
	lock.unlock();
}

void Thread::sleep(uint32 utime) {
	if (utime) {
		lock.lock();
		
		if (queue <= QUEUE_MAX_PRIORITY) {
			QUEUE_LOCK_REMOVE(this, queue);
			
			queue = QUEUE_SLEEP;
			sleepTime = utime;
			
			QUEUE_LOCK_APPEND(this, queue);
			
			lock.unlock();
			switchThreads();
			
			return;
		}
		
		lock.unlock();
	}
}

void Thread::yield(void) {
	lock.lock();
	
	if (queue <= QUEUE_MAX_PRIORITY) {
		lock.unlock();
		switchThreads();
		
		return;
	}
	
	lock.unlock();
}

/**
 * Provides default thread related fault handling that can be overridden by
 * subclasses of Thread to handle their speific requirements. For instance,
 * UserThread needs to handle page faults in the user stack region to grow the
 * user stack; VM86Thread needs to handle general protection faults to emulate
 * protected instruction behavior.
 */
bool Thread::fault(regs_t *regs) {
	bool ret = false;
	
	switch (regs->which_int) {
		case INT_DEV_NA_EX:
			clearInCR0(X86_CR0_TS); // don't fault on fpu/mmx/sse

			if (lastFpuThread) {
				// save the context of the thread last to use the fpu
				fpuContextSave(lastFpuThread->fpuContext);
				lastFpuThread->fpuState |= FPU_STATE_SAVED;
			}
			
			if (fpuState == FPU_STATE_UNUSED) {
				// save this thread as the new last fpu user
				lastFpuThread = this;
				
				// update the state
				fpuState = FPU_STATE_USED;
				
				// now init the fpu context
				fpu_init();
			}
			
			ret = true;
			break;
		
		case INT_PAGE_FAULT:
			if (getCR2() == 0xffffffff) {
				printf("Thread exit recognized: %s\n", name);
			} else {
				printf("Page fault at %08x to %08x in thread %s\n", regs->eip,
					getCR2(), name);
				/*Log::printf(LOG_HIGH, "Page fault at %08x to %08x in thread %s"
					"\n", regs->eip, getCR2(), name);*/
				
				uint32 to = getCR2();
				page_entry_t code;
				code.page = (void *) regs->err_code;
				
				Log::printf(LOG_LOW, "Page fault at %08x to %08x %s! "
					"[%c %c %c]\n",
					regs->eip, to, this->name,
					code.present ? 'p' : ' ',
					code.rw ? 'w' : 'r',
					code.user_priv ? 'u' : 's');
			}
			
			this->kill();
			ret = true;
			break;
		
		case INT_GP_FAULT:
			printf("General protection fault at %08x to %08x code %08x "
				"in thread %s\n", regs->eip, getCR2(), regs->err_code, name);
			printf("    DS     %04x ES     %04x\n", regs->ds, regs->es);
			
			this->kill();
			ret = true;
			break;
	}
	
	return ret;
}

bool UserThread::fault(regs_t *regs) {
	bool ret = false;
	
	switch (regs->which_int) {
		case INT_PAGE_FAULT:
			if (adjustStack(regs)) {
				ret = true;
			} else {
				ret = Thread::fault(regs);
			}
			break;
		
		default:
			ret = Thread::fault(regs);
			break;
	}
	
	return ret;
}			

bool VM86Thread::fault(regs_t *regs) {
	bool ret = false;
	
	switch (regs->which_int) {
		case INT_GP_FAULT:
			if ((regs->eflags & 0x20000)
				&& emulateInstruction((regs_vm86_t *) regs)) {
				ret = true;
			} else {
				#if 1
				Log::printf(LOG_LOW, "%s:%d: regs_t at %08x\n", __FILE__,
					__LINE__, (uint32) regs);
				Log::hexdump(LOG_LOW, (uint8 *) regs, 1024, true);
				return false;
				#elif 0
				regs_vm86_t *vmregs = (regs_vm86_t *) regs;
				
				printf("General Protection Fault in thread %s: code %08x\n",
					name, regs->err_code);
				printf(
					" CS:     %04x EIP: %08x EFL: %08x CR2: %08x\n",
					regs->cs, regs->eip, regs->eflags, getCR2());
				printf(
					"EAX: %08x ECX: %08x EDX: %08x EBX: %08x\n"
					"ESP: %08x EBP: %08x ESI: %08x EDI: %08x\n",
					regs->eax, regs->ecx, regs->edx, regs->ebx, regs->user_esp,
					regs->ebp, regs->esi, regs->edi);
				printf(
					" DS:     %04x  ES:     %04x  FS:     %04x  GS:     %04x\n",
					vmregs->vm_ds, vmregs->vm_es, vmregs->vm_fs, vmregs->vm_gs);
				printf(
					" VM SS:  %04x  VM ESP: %04x\n", vmregs->vm_ss,
					vmregs->vm_esp);
				
				uint16 *stack = (uint16 *) ((vmregs->vm_ss << 4)
					+ vmregs->vm_esp);
				uint32 *stack32 = (uint32 *) stack;
				
				printf("%04x %04x %04x %04x %04x %04x\n", stack[0], stack[1],
					stack[2], stack[3], stack[4], stack[5]);
				printf("%04x %04x %04x %04x %04x %04x\n", stack[6], stack[7],
					stack[8], stack[9], stack[10], stack[11]);
				printf("%04x %04x %04x %04x %04x %04x\n", stack[12], stack[13],
					stack[14], stack[15], stack[16], stack[17]);
				
				Log::printf(LOG_LOW, "%s:%d: regs_t at %08x\n", __FILE__,
					__LINE__, (uint32) regs);
				Log::hexdump(LOG_LOW, (uint8 *) regs, 1024, true);
				
				this->kill();
				ret = true;
				#else
				ret = Thread::fault(regs);
				#endif
			}
			break;
		
		case INT_STACK_FAULT: {
			// check for stack underflow caused by a ret
			uint8 in = *(uint8 *) (regs->eip + (regs->cs << 4));
			bool isRet = in == 0xc2 || in == 0xc3 || in == 0xca
				|| in == 0xcb;
			
			//printf("Stack fault in vm86 thread: sp=%04x ins=%02x\n",
			//	regs->user_esp, in);
			
			if (regs->user_esp == 0xfffe && isRet) {
				printf("vm86 thread exit recognized\n");
				
				//removeCurrentThread(regs);
				this->kill();
				ret = true;
			} else {
				ret = Thread::fault(regs);
			}
		}
		break;	
		
		default:
			ret = Thread::fault(regs);
			break;
	}
	
	return ret;
}

/*
 * Emulates an IOPL sensitive instruction for a vm86 mode task.
 * Returns true if the instruction could be emulated, false if not or if a fault
 * should occur.
 */

// register encodings
enum {
	VM86_REG_AX=0,
	VM86_REG_CX,
	VM86_REG_DX,
	VM86_REG_BX,
	VM86_REG_SP,
	VM86_REG_BP,
	VM86_REG_SI,
	VM86_REG_DI,
};

extern "C"
void interrupt_handler(regs_t *regs);
	
bool VM86Thread::emulateInstruction(regs_vm86_t *regs) {
	// the IVT starts at 0x00000000 in the current addr space
	uint16 *ivt = (uint16 *) 0;
	
	uint16 *stack = (uint16 *) ((regs->vm_ss << 4) + regs->vm_esp);
	uint32 *stack32 = (uint32 *) stack;
	
	uint8 *ip = (uint8 *) ((regs->cs << 4) + regs->eip);
	
	bool operand32 = false;
	bool address32 = false;
	
	while(true) {
		switch (ip[0]) {
			case 0xcc:		// INT3 (terminate)
				Log::printf(LOG_LOW, "vm86: breakpoint\n");
				return false;
				
			case 0xcd:		// INT n
				switch (ip[1]) {
					case 0x30: {	// syscall
						/*printf("vm86: syscall %x subfunction %x\n", stack32[0],
							stack32[1]);*/
						
						// advance eip
						regs->eip = (uint16) (regs->eip + 2);
						
						// patch through syscalls
						uint32 ret = syscall(stack32[0], stack32[1], stack32[2],
							stack32[3], stack32[4], stack32[5], stack32[6],
							stack32[7]);
						
						regs->eax = ret & 0xffff;
						regs->edx = ret >> 16;
						
						/*regs_t tmp;
						
						// copy over the basic structure
						memcpy(&tmp, regs, sizeof(regs_t));
							
						// translate to linear task equiv
						tmp.user_esp = (uint32) stack32 - 4; // sub for ret addr
						tmp.cs = USER_CODE_SELECTOR;
						tmp.eip = (uint32) ip + 2;
						tmp.which_int = INT_SYSCALL;
						
						// pass in for processing
						interrupt_handler(&tmp);
						
						#if 0
						// copy back only the values that could be modified
						regs->eax = tmp.eax; // return value (32bit/64bit low)
						regs->edx = tmp.edx; // return value (64bit high)
						#else
						// NOTE: BCC uses dx:ax for 32bit return values
						// 64bit return values are unsupported
						regs->eax = tmp.eax & 0x0000ffff;
						regs->edx = tmp.eax >> 16;
						#endif
						
						// syscall could have switched tasks, by changing this
						//regs->task_esp = tmp.task_esp;
						
						// NOTE: for now we don't patch eip in any fault handler
						// it may be necessary to handle eip mods in the future
						*/
						return true;
					}
					
					default:
						stack -= 3;
						regs->vm_esp = ((regs->vm_esp & 0xffff) - 6) & 0xffff;
						
						stack[0] = (uint16) (regs->eip + 2);
						stack[1] = regs->cs;
						stack[2] = (uint16) regs->eflags;
						
						if (vif) {
							stack[2] |= 0x200;
						} else {
							stack[2] &= ~0x200;
						}
						
						vif = false;
						regs->cs = ivt[ip[1]*2 + 1];
						regs->eip = ivt[ip[1]*2];
						
						//printf("vm86: INT %xh vectoring to %04x:%04x\n",
						//	ip[1], regs->cs, regs->eip);
						
						return true;
				}
			
			case 0xcf:		// IRET
				regs->eip = stack[0];
				regs->cs = stack[1];
				regs->eflags = stack[2] | 0x20200; // make sure VM = 1, IF =1
				vif = stack[2] & 0x200; // store IF in vif
				
				regs->vm_esp = ((regs->vm_esp & 0xffff) + 6) & 0xffff;
				
				return true;
			
			case 0xfa:		// CLI
				vif = false;
				regs->eip = (uint16) regs->eip + 1;
				
				return true;
			
			case 0xfb:		// STI
				vif = true;
				regs->eip = (uint16) regs->eip + 1;
				
				return true;
			
			case 0x66:		// O32
				operand32 = true;
				ip++;
				regs->eip = (uint16) regs->eip + 1;
				
				break;
			
			case 0x67:		// A32
				address32 = true;
				ip++;
				regs->eip = (uint16) regs->eip + 1;
				
				break;
			
			case 0x9c:		// PUSHF
				if (operand32) {
					regs->vm_esp = ((regs->vm_esp & 0xffff) - 4) & 0xffff;
					stack32--;
					stack32[0] = regs->eflags & 0xdff; // mask valid flags
					
					if (vif) {
						stack32[0] |= 0x200;
					} else {
						stack32[0] &= ~0x200;
					}
				} else {
					regs->vm_esp = ((regs->vm_esp & 0xffff) - 2) & 0xffff;
					stack--;
					stack[0] = regs->eflags;
					
					if (vif) {
						stack[0] |= 0x200;
					} else {
						stack[0] &= ~0x200;
					}
				}
				
				regs->eip = (uint16) regs->eip + 1;
				
				return true;
			
			case 0x9d:		// POPF
				if (operand32) {
					regs->eflags = (stack32[0] & 0xdff) | 0x20200; // VM = 1, IF = 1, mask valid
					vif = stack32[0] & 0x200;
					regs->vm_esp = ((regs->vm_esp & 0xffff) + 4) & 0xffff;
				} else {
					regs->eflags = stack[0] | 0x20200;
					vif = stack[0] & 0x200;
					regs->vm_esp = ((regs->vm_esp & 0xffff) + 2) & 0xffff;
				}
				
				regs->eip = (uint16) regs->eip + 1;
				
				return true;
			
			case 0x58:		// POP
			case 0x59:
			case 0x5a:
			case 0x5b:
			case 0x5c:
			case 0x5d:
			case 0x5e:
			case 0x5f:
				if (address32) {
					regs->vm_esp = ((regs->vm_esp & 0xffff) + 4) & 0xffff;
				} else {
					regs->vm_esp = ((regs->vm_esp & 0xffff) + 2) & 0xffff;
				}
				
				uint32 val;
				if (operand32) {
					val = stack32[0];
				} else {
					val = stack[0];
				}
					
				switch (ip[0] & 0x7) {
					case VM86_REG_AX:
						regs->eax = val;
						break;
					
					case VM86_REG_CX:
						regs->ecx = val;
						break;
					
					case VM86_REG_DX:
						regs->edx = val;
						break;
					
					case VM86_REG_BX:
						regs->ebx = val;
						break;
					
					case VM86_REG_SP:
						regs->vm_esp = val & 0xffff;
						break;
					
					case VM86_REG_BP:
						regs->ebp = val;
						break;
					
					case VM86_REG_SI:
						regs->esi = val;
						break;
					
					case VM86_REG_DI:
						regs->edi = val;
						break;
				}
					
				regs->eip = (uint16) regs->eip + 1;
				
				return true;
				
			default:
				Log::printf(LOG_LOW, "vm86: Unrecognized op code: %02x at "
					"%04x:%04x\n", ip[0], regs->cs, regs->eip);
					
				return false;
		}
	}
}

/**
 * General fault handler. If a fault requires a class (Thread, UserThread, ...)
 * specific response, this handler will delegate the fault to the instance.
 */
bool Thread::faultHandler(regs_t *regs) {
	// don't use a Ref here, since this handler may not return
	Thread *thread = getCurrentThread();
	bool ret = false;
	
	switch (regs->which_int) {
		case INT_PAGE_FAULT:
		case INT_GP_FAULT:
		case INT_STACK_FAULT:
		case INT_DIVIDE_0:
		case INT_INVALID_OP:
		case INT_DEBUG_EX:
		case INT_DEV_NA_EX: {
			/* for debugging problems with switchThreads()
			seg_sel_t ss = 0;
			__asm__ (
				"mov %%ss,%%eax"
				: "=a"(ss)
			);
			
			if (ss == systemStackSelector) {
				panic("Stack segment == %02x", ss);
			}*/
			
			ret = thread->fault(regs);
		}
		break;
	}
	
	return ret;
}

/**
 * Provides a properly formed stack image for switching stacks and then calls
 * reschedule.
 *
 * For simplicity and felxibility, the same layout is used for the interrupt
 * handler stub and the arbitrary stack switching mechanism. This allows for
 * switching to a thread suspended by an interrupt from a non-interrupt
 * stack frame, and vice versa.
 */
void Thread::switchThreads(void) {
	InterruptLock barrier;
	
	barrier.lock();
	__asm__ __volatile__ (
		"pushf				\n\t"
		"push %%cs			\n\t"
		"push $1f			\n\t"
		"push $0			\n\t"
		"push $0			\n\t"
		"push %%gs			\n\t"
		"push %%fs			\n\t"
		"push %%es			\n\t"
		"push %%ds			\n\t"
		"pusha				\n\t"
		"mov %%esp,%%eax	\n\t"
		"push %%eax			\n\t"
		"mov %%esp,%%eax	\n\t"
		"push %%eax			\n\t"
		"call *%0			\n\t"
		"pop %%eax			\n\t"
		"pop %%eax			\n\t"
		"mov %%eax,%%esp	\n\t"
		"popa				\n\t"
		"pop %%ds			\n\t"
		"pop %%es			\n\t"
		"pop %%fs			\n\t"
		"pop %%gs			\n\t"
		"add $8,%%esp		\n\t"
		"iret				\n\t"
		"1:	"
		:
		: "c" (reschedule)
		:
	);
	barrier.unlock();
}

/**
 * Reschedule ready threads according to priority.
 *
 * General algorithm:
 * Examin each priority level for an available thread starting at the highest.
 * If the thread at the head of the highest priority, non-empty list is the
 * current thread, and there is another thread in the same list, move the
 * current thread to the end of the list and the next thread becomes the current
 * thread. If the current thread is the only thread in the first highest non-
 * empty list, then it remains the current thread for the next quantum.
 * Otherwise, if there is a thread, that is not the current thread, at the head
 * of a higher priority list than the current thread, that higher priority
 * thread becomes the current thread.
 */
void Thread::reschedule(regs_t *regs) {
	if (!currentThread) {
		bug("currentThread is null on reschedule");
	}
	
	//rescheduleLock.lock();
	
	for (int i=QUEUE_MAX_PRIORITY; i >= QUEUE_IDLE; i--) {
		queueLocks[i].lock();
		
		if (currentThread == currentThread->next) {
			bug("Queue corruption in queue %d, thread '%s'", i,
				currentThread->name);
		}
		
		if (head[i] == currentThread) {
			if (currentThread->next) {
				// switch tasks according to the scheduling algorithm
				// round robin for now
				queueRemove(currentThread, i);
				queueAppend(currentThread, i);
				
				currentThread->save(regs);
				currentThread = head[i];
				currentThread->restore(regs);
			} else {
				// if the current thread is the only thread at this priority,
				// it will continue as the current thread
			}
			
			queueLocks[i].unlock();
			break;
		} else if (head[i]) {
			// there is another thread at the head of the queue at this priority
			// switch to it immediately
			currentThread->save(regs);
			currentThread = head[i];
			currentThread->restore(regs);
			
			queueLocks[i].unlock();
			break;
		}
		
		queueLocks[i].unlock();
	}
	
	//rescheduleLock.unlock();
}

bool Thread::tickHandler(regs_t *regs) {
	static uint64 lastTime = 0;
	uint64 time = Timer::getMicroTime();
		
	uint64 tdelta = time - lastTime;
	lastTime = time;
	
	// check for any threads that need to wake up
	// TODO: incrementally bubble threads with shorter remaining sleep times to
	// the front of the list
	queueLocks[QUEUE_SLEEP].lock();
	
	for (Thread *t = head[QUEUE_SLEEP]; t;) {
		Thread *tmp = t->next;
		
		if (t->sleepTime <= tdelta) {
			// return the thread to the ready queue
			queueRemove(t, QUEUE_SLEEP);
			
			t->queue = t->getFloatingPriority();
		
			QUEUE_LOCK_PREPEND(t, t->queue);
			
		} else {
			t->sleepTime -= tdelta;
		}
		
		t = tmp;
	}
	
	queueLocks[QUEUE_SLEEP].unlock();
	
	reschedule(regs);
	return true;
}

#define _CLASS (syscallParam(regs, 0))
#define _FUNC (syscallParam(regs, 1))
#define _ARG(n) (syscallParam(regs, (n)+2))

bool Thread::syscallHandler(regs_t *regs) {
	bool handled = false;
	
	if (_CLASS == SYSCALL_THREAD) {
		switch(_FUNC) {
			case SYSCALL_THREAD_YIELD:
				if (currentThread) {
					currentThread->yield();
				} else {
					bug("%s:%d currentThread is null", __FILE__, __LINE__);
				}
				break;
			
			case SYSCALL_THREAD_NEW:
				if (currentThread) {
					switch (currentThread->type) {
						case THREAD_TYPE_KERNEL: {
							RefThread thread = Thread::createThread(
								(ThreadEntryPoint) _ARG(0), (void *) _ARG(1),
								(char *) _ARG(2), currentThread->process);
							thread->run();
						}
						break;
						
						case THREAD_TYPE_USER: {
							RefThread thread = UserThread::createThread(
								(ThreadEntryPoint) _ARG(0), (void *) _ARG(1),
								(char *) _ARG(2), currentThread->process);
							thread->run();
						}
						break;
						
						default:
							bug("%s:%d unsupported or invalid thread type",
								__FILE__, __LINE__);
					}
				} else {
					bug("%s:%d currentThread is null", __FILE__, __LINE__);
				}
				break;
			
			case SYSCALL_THREAD_EXIT:
				if (currentThread) {
					currentThread->kill();
					// TODO: preserve exit code and notify joined threads
				} else {
					bug("%s:%d currentThread is null", __FILE__, __LINE__);
				}
				break;
			
			case SYSCALL_THREAD_KILL:
				if (currentThread) {
					// handle killing self differently to avoid keeping a ref
					// active on the dying stack, thus binding up resources
					if (_ARG(0) == currentThread->tid) {
						currentThread->kill();
					} else {
						RefProcess proc = Process::getCurrentProcess();
						RefThread thread(0);
						if (proc->threads.get(_ARG(0), thread)) {
							thread->kill();
						}
					}
				} else {
					bug("%s:%d currentThread is null", __FILE__, __LINE__);
				}
				break;
			
			case SYSCALL_THREAD_JOIN:
				break;
			
			case SYSCALL_THREAD_WAIT:
				if (currentThread) {
					currentThread->wait();
				} else {
					bug("%s:%d currentThread is null", __FILE__, __LINE__);
				}
				break;
			
			case SYSCALL_THREAD_NOTIFY:
				{
					RefThread thread = nullThread;
					if (threadList.get(_ARG(0), thread) && thread) {
						thread->notify();
					}
				}
				break;
			
			case SYSCALL_THREAD_SLEEP:
				if (currentThread) {
					currentThread->sleep(_ARG(0) * 1000);
				} else {
					bug("%s:%d currentThread is null", __FILE__, __LINE__);
				}
				break;
			
			case SYSCALL_THREAD_USLEEP:
				if (currentThread) {
					currentThread->sleep(_ARG(0));
				} else {
					bug("%s:%d currentThread is null", __FILE__, __LINE__);
				}
				break;
			
			case SYSCALL_THREAD_GET_TID:
				if (currentThread) {
					regs->eax = currentThread->getTid();
				} else {
					regs->eax = (uint) -1;
				}
				break;
			
			case SYSCALL_THREAD_INFO:
				break;
			
			case SYSCALL_THREAD_GET_LOCAL:
				if (currentThread) {
					if (currentThread->type == THREAD_TYPE_USER) {
						UserThread *user = (UserThread *) currentThread;
						regs->eax = (uint32) user->getLocal(_ARG(0));
					}
				} else {
					bug("%s:%d currentThread is null", __FILE__, __LINE__);
				}
				break;
			
			case SYSCALL_THREAD_SET_LOCAL:
				if (currentThread) {
					if (currentThread->type == THREAD_TYPE_USER) {
						UserThread *user = (UserThread *) currentThread;
						
						user->setLocal(_ARG(0), (void *) _ARG(1));
						regs->eax = 0;
					}
				} else {
					bug("%s:%d currentThread is null", __FILE__, __LINE__);
				}
				break;
			
			case SYSCALL_THREAD_NEW_KEY:
				if (currentThread) {
					if (currentThread->type == THREAD_TYPE_USER) {
						UserThread *user = (UserThread *) currentThread;
						
						regs->eax = (uint32) user->createKey((void *) _ARG(0),
							(void (*)(void *)) _ARG(1));
					}
				} else {
					bug("%s:%d currentThread is null", __FILE__, __LINE__);
				}
				break;
			
			case SYSCALL_THREAD_FREE_KEY:
				if (currentThread) {
					if (currentThread->type == THREAD_TYPE_USER) {
						UserThread *user = (UserThread *) currentThread;
						
						user->destroyKey(_ARG(0));
					}
				} else {
					bug("%s:%d currentThread is null", __FILE__, __LINE__);
				}
				break;
			
			case SYSCALL_THREAD_STACK_CHECK:
				break;
			
			default:
				Log::printf(LOG_MEDIUM, "Unrecognized SYSCALL_THREAD function "
					"%u\n", _FUNC);
				break;
		}
		
		handled = true;
	}
	
	return handled;
}

static tid_t getNextTid(void) {
	while (threadList.containsHandle(nextTid)) {
		nextTid++;
	}
	
	return nextTid;
}
